﻿using System;
using System.Windows;
using System.Collections.Generic;

namespace New3DDemo
{
    public static class LineHelper
    {
        /// <summary>
        /// 求点到直线的距离
        /// </summary>
        public static double GetDistance(LineFunc lf, Point p)
        {
            if (lf.IsPassed(p)) return 0;
            LineFunc cz = new LineFunc(p, lf.Angle + Math.PI / 2);
            return GetDistance(p, lf.GetIntersection(cz).Value);
        }

        /// <summary>
        /// 求两点的距离
        /// </summary>
        public static double GetDistance(Point x, Point y)
        {
            return Math.Sqrt(Math.Pow(x.X - y.X, 2) + Math.Pow(x.Y - y.Y, 2));
        }

        /// <summary>
        /// 根据两个点获取这个方向的夹角弧度值
        /// </summary>
        public static double GetAngle(Point begin, Point end)
        {
            LineFunc lf = new LineFunc(begin, end);
            double result = lf.Angle;
            if (end.X < begin.X || (end.X == begin.X && end.Y < begin.Y)) result += Math.PI;

            return AngleAdjustment(result);
        }

        /// <summary>
        /// 根据三点，获取角平分线
        /// </summary>
        public static LineFunc GetAngleBisector(Point left, Point mid, Point right)
        {
            double angel1 = GetAngle(mid, left);
            double angel2 = GetAngle(mid, right);

            return new LineFunc(mid, (angel1 + angel2) / 2);
        }

        /// <summary>
        /// 获得垂直于lf，且经过p点的线
        /// </summary>
        public static LineFunc GetVerticalLine(LineFunc lf, Point p)
        {
            return new LineFunc(p, lf.Angle + Math.PI / 2);
        }

        /// <summary>
        /// 获取从start点开始，沿angle方向，距离distance的一个点
        /// </summary>
        public static Point GetPoint(Point start, double angle, double distance)
        {
            double cos = distance * Math.Cos(angle);
            double sin = distance * Math.Sin(angle);
            return new Point(start.X + cos, start.Y + sin);
        }

        /// <summary>
        /// 将角度值归到[PI, -PI)区间
        /// </summary>
        public static double AngleAdjustment(double angle)
        {
            while (angle > Math.PI) angle -= Math.PI * 2;
            while (angle <= -Math.PI) angle += Math.PI * 2;
            return angle;
        }

        /// <summary>
        /// 获取从start点开始，沿angle方向，跟line相交的距离
        /// </summary>
        public static double GetLineDistance(Point start, double angle, LineFunc line)
        {
            if (line.IsPassed(start)) return 0;
            LineFunc lf = new LineFunc(start, angle);
            if (lf.IsParallel(line)) return double.MaxValue;
            Point p = lf.GetIntersection(line).Value;

            if (GetAngle(start, p) - angle <= 0.000001)
                return GetDistance(start, p);
            else
                return -GetDistance(start, p);
        }

        /// <summary>
        /// 获取角平分线序列
        /// </summary>
        public static List<LineFunc> GetAngleBisectorCollection(IList<Point> points)
        {
            if (points.Count < 2) return null;

            List<LineFunc> result = new List<LineFunc>();
            LineFunc lf = new LineFunc(points[0], points[1]);
            result.Add(GetVerticalLine(lf, points[0]));

            for (int i = 1; i < points.Count - 1; i++)
            {
                result.Add(GetAngleBisector(points[i - 1], points[i], points[i + 1]));
            }

            lf = new LineFunc(points[points.Count - 2], points[points.Count - 1]);
            result.Add(GetVerticalLine(lf, points[points.Count - 1]));

            return result;
        }

        /// <summary>
        /// 根据points点序列，生成相距distance的平行折线点序列(去倒角)
        /// </summary>
        public static List<Point> MakeParallelLine(IList<Point> points, double distance)
        {
            List<Point> result = MakeParallelLine2(points, distance);
            if (result != null)
            {
                for (int i = 0; i < result.Count - 3; i++)
                {
                    LineFunc lf1 = new LineFunc(result[i], result[i + 1]);
                    for (int j = i + 2; j < result.Count - 1; j++)
                    {
                        LineFunc lf2 = new LineFunc(result[j], result[j + 1]);
                        if (lf1.IsParallel(lf2)) continue;
                        Point p = lf1.GetIntersection(lf2).Value;
                        double dis1 = GetDistance(p, result[i]);
                        double dis2 = GetDistance(p, result[i + 1]);
                        double dis3 = GetDistance(p, result[j]);
                        double dis4 = GetDistance(p, result[j + 1]);
                        if (dis1 > 0 && dis2 > 0 && dis3 > 0 && dis4 > 0 &&
                            dis1 + dis2 - GetDistance(result[i], result[i + 1]) <= 0.000001 &&
                            dis3 + dis4 - GetDistance(result[j], result[j + 1]) <= 0.000001)
                        {
                            result[i + 1] = p;
                            for (int k = i + 2; k < j + 1; k++)
                            {
                                result.RemoveAt(i + 2);
                            }
                            i--;
                            break;
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 根据points点序列，生成相距distance的平行折线点序列(带倒角)
        /// </summary>
        public static List<Point> MakeParallelLine2(IList<Point> points, double distance)
        {
            if (distance == 0) return new List<Point>(points);
            if (points.Count < 2) return null;

            List<Point> result = new List<Point>();
            List<LineFunc> lines = GetAngleBisectorCollection(points);

            double angle = GetAngle(points[0], points[1]);
            Point p = GetPoint(points[0], angle + Math.PI / 2, distance);

            result.Add(p);
            LineFunc lf = new LineFunc(p, angle);

            for (int i = 1; i < lines.Count - 1; i++)
            {
                Point p2 = lines[i].GetIntersection(lf).Value;
                result.Add(p2);
                lf = new LineFunc(p2, GetAngle(points[i], points[i + 1]));
            }
            result.Add(lines[lines.Count - 1].GetIntersection(lf).Value);

            return result;
        }
    }
}
